﻿using System;
using System.Linq.Expressions;
using Fuse8.DomainFramework.Common;
using Fuse8.DomainFramework.Common.Locator;
using Fuse8.DomainFramework.DataAccess;
using Fuse8.DomainFramework.Domain;
using Fuse8.DomainFramework.BusinessService;

using Fuse8.ContentManagementFramework.Domain;

namespace Fuse8.ContentManagementFramework.BizFacade.Services
{
    public abstract class CmsBusinessService<T> : CmsBusinessService<T, int>
        where T : class, IEntityObject<int>, new()
    {
        #region Fields

        private IEntityBusinessService<CmsEntityMeta, int> _entityMetaService;

        #endregion

        #region Properties

        protected virtual IEntityBusinessService<CmsEntityMeta, int> MetaService
        {
            get
            {
                if (_entityMetaService == null)
                {
                    _entityMetaService = new BusinessServiceFacade<CmsEntityMeta, int>();
                }

                return _entityMetaService;
            }
        }

        protected ICmsContext CmsContext
        {
            get
            {
                return DependencyServiceLocator.Current.GetInstance<ICmsContext>();
            }
        }

        #endregion

        #region Override Add, Update, Delete methods

        public override void Add(T entityObject)
        {
            Guard.ArgumentNotNull(entityObject, "entityObject");

            base.Add(entityObject);

            var cmsEntityObject = entityObject as CmsComplexEntityObject;
            if (IsUseBuildInFlow(cmsEntityObject))
            {
                //Entity metas
                var metas = cmsEntityObject.Metas;
                foreach (var meta in metas)
                {
                    if (meta.IsNew)
                    {
                        //need correct id because of entity was added
                        meta.EntityId = entityObject.Id;

                        MetaService.Add(meta);
                    }
                }
                cmsEntityObject.Metas = metas;
            }
        }

        public override void Save(T entityObject)
        {
            Guard.ArgumentNotNull(entityObject, "entityObject");

            base.Save(entityObject);
            
            var cmsEntityObject = entityObject as CmsComplexEntityObject;
            if (IsUseBuildInFlow(cmsEntityObject))
            {
                //Entity metas
                var metas = cmsEntityObject.Metas;
                foreach (var meta in metas)
                {
                    if (meta.IsNew)
                    {
                        MetaService.Add(meta);
                    }
                    else
                    {
                        MetaService.Save(meta);
                    }
                }
            }
        }

        public override void Remove(T entityObject)
        {
            Guard.ArgumentNotNull(entityObject, "entityObject");

            base.Remove(entityObject);

            var cmsEntityObject = entityObject as CmsComplexEntityObject;
            if (IsUseBuildInFlow(cmsEntityObject))
            {
                //Entity metas
                var metas = cmsEntityObject.Metas;
                foreach (var meta in metas)
                {
                    if (meta.IsCreated)
                    {
                        MetaService.Remove(meta);
                    }
                }
                cmsEntityObject.Metas = null;
            }
        }

        #endregion

        #region Private members
        private bool IsUseBuildInFlow (CmsComplexEntityObject entityObject)
        {
            return ((entityObject != null) && entityObject.UseBuiltInFlow && CmsContext.UseBuiltInFlow);
        }
        #endregion

    }
    
    public abstract class CmsBusinessService<TEntity, TKey> : BaseBusinessService<TEntity, TKey>
        where TEntity : class, IEntityObject<TKey>, new()
        where TKey : IComparable<TKey>, IEquatable<TKey>
    {
        #region Before actions

        protected override void OnCreating(EntityPreEventArgs<TEntity, TKey> e)
        {
            Guard.ArgumentNotNull(e, "EntityPreEventArgs");

            if (e.Entity is ITrackingEntityObject)
            {
                TrackingEntityObjectMediator.TrackEntity(e.Entity as ITrackingEntityObject);
            }

            base.OnCreating(e);
        }

        protected override void OnUpdating(EntityPreEventArgs<TEntity, TKey> e)
        {
            Guard.ArgumentNotNull(e, "EntityPreEventArgs");

            if (e.Entity is ITrackingEntityObject)
            {
                TrackingEntityObjectMediator.TrackEntity(e.Entity as ITrackingEntityObject);
            }

            base.OnUpdating(e);
        }        

        #endregion
    }
}
